package com.gitee.wsl.android.search.application

import android.content.ActivityNotFoundException
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.pm.ActivityInfo
import android.content.pm.ApplicationInfo
import android.content.pm.LauncherActivityInfo
import android.content.pm.LauncherApps
import android.content.pm.PackageManager
import android.graphics.drawable.AdaptiveIconDrawable
import android.net.Uri
import android.os.Bundle
import android.os.Process
import android.os.UserHandle
import androidx.core.content.FileProvider
import androidx.core.content.getSystemService
import com.gitee.wsl.android.R
import com.gitee.wsl.android.drawable.icon.ColorLayer
import com.gitee.wsl.android.drawable.icon.LauncherIcon
import com.gitee.wsl.android.drawable.icon.StaticIconLayer
import com.gitee.wsl.android.drawable.icon.StaticLauncherIcon
import com.gitee.wsl.android.drawable.icon.TintedIconLayer
import com.gitee.wsl.android.drawable.icon.TransparentLayer
import com.gitee.wsl.android.ext.device.VersionExt.isAtLeastApiLevel
import com.gitee.wsl.android.ext.device.getInstallSource
import com.gitee.wsl.android.ext.getSerialNumber
import com.gitee.wsl.android.search.AppProfile
import com.gitee.wsl.android.search.Application
import com.gitee.wsl.android.search.StoreLink
import com.gitee.wsl.api.SearchableSerializer
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.File


internal data class LauncherApp(
    private val launcherActivityInfo: LauncherActivityInfo,
    override val versionName: String?,
    override val isSuspended: Boolean = false,
    internal val userSerialNumber: Long,
    override val labelOverride: String? = null,
    override val iconId: Int= R.drawable.ic_file_android
) : Application {

    override val componentName: ComponentName
        get() = launcherActivityInfo.componentName

    override val label: String = launcherActivityInfo.label.toString()


    constructor(context: Context, launcherActivityInfo: LauncherActivityInfo) : this(
        launcherActivityInfo,
        versionName = getPackageVersionName(context, launcherActivityInfo.applicationInfo.packageName),
        userSerialNumber = launcherActivityInfo.user.getSerialNumber(context)
    )

    override val user: UserHandle
        get() = launcherActivityInfo.user

    private val isMainProfile = launcherActivityInfo.user == Process.myUserHandle()

    override val profile: AppProfile
        get() = if (isMainProfile) AppProfile.Personal else AppProfile.Work

    override val isSystemApp: Boolean = launcherActivityInfo.applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM != 0

    override val canUninstall: Boolean
        get() = !isSystemApp && isMainProfile

    override val domain: String = Domain
    override val preferDetailsOverLaunch: Boolean = false

    override fun overrideLabel(label: String): LauncherApp {
        return this.copy(labelOverride = label)
    }

    override val key: String
        // For backwards compatibility, user serial number is not included in main profile
        get() = if (isMainProfile) "${domain}://${componentName.packageName}:${componentName.className}"
        else "${domain}://${componentName.packageName}:${componentName.className}:${userSerialNumber}"


    override suspend fun loadIcon(
        context: Context,
        size: Int,
        themed: Boolean,
    ): LauncherIcon? {
        try {
            val icon =
                withContext(Dispatchers.IO) {
                    launcherActivityInfo.getIcon(context.resources.displayMetrics.densityDpi)

                } ?: return null
            if (icon is AdaptiveIconDrawable) {
                if (themed && isAtLeastApiLevel(33) && icon.monochrome != null) {
                    return StaticLauncherIcon(
                        foregroundLayer = TintedIconLayer(
                            scale = 1.5f,
                            icon = icon.monochrome!!,
                        ),
                        backgroundLayer = ColorLayer()
                    )
                }
                return StaticLauncherIcon(
                    foregroundLayer = icon.foreground?.let {
                        StaticIconLayer(
                            icon = it,
                            scale = 1.5f,
                        )
                    } ?: TransparentLayer,
                    backgroundLayer = icon.background?.let {
                        StaticIconLayer(
                            icon = it,
                            scale = 1.5f,
                        )
                    } ?: TransparentLayer,
                )
            } else {
                return StaticLauncherIcon(
                    foregroundLayer = StaticIconLayer(
                        icon = icon,
                        scale = 1f,
                    ),
                    backgroundLayer = TransparentLayer
                )
            }
        } catch (e: PackageManager.NameNotFoundException) {
            return null
        }
    }

    override fun launch(context: Context, options: Bundle?): Boolean {
        val launcherApps = context.getSystemService<LauncherApps>()!!
        if (isAtLeastApiLevel(31)) {
            options?.putInt("android.activity.splashScreenStyle", 1)
        }
        try {
            launcherApps.startMainActivity(
                componentName,
                launcherActivityInfo.user,
                null,
                options
            )
        } catch (e: SecurityException) {
            return false
        } catch (e: ActivityNotFoundException) {
            return false
        }
        return true
    }

    override fun getStoreDetails(context: Context): StoreLink? {
        val pm = context.packageManager
        return try {
            val installSourceInfo = pm.getInstallSource(componentName.packageName)
            getStoreLinkForInstaller(
                installSourceInfo.initiatingPackageName,
                componentName.packageName
            )
        } catch (e: PackageManager.NameNotFoundException) {
            null
        } catch (e: IllegalArgumentException) {
            null
        }
    }

    override fun uninstall(context: Context) {
        val intent = Intent(Intent.ACTION_DELETE)
        intent.data = Uri.parse("package:${componentName.packageName}")
        context.startActivity(intent)
    }

    override fun openAppDetails(context: Context) {
        val launcherApps = context.getSystemService<LauncherApps>()!!

        launcherApps.startAppDetailsActivity(
            componentName,
            user,
            null,
            null
        )
    }

    override val canShareApk: Boolean = true
    override suspend fun shareApkFile(context: Context) {
        val launcherApps = context.getSystemService<LauncherApps>()!!
        val fileCopy = java.io.File(
            context.cacheDir,
            "${componentName.packageName}-${versionName}.apk"
        )
        withContext(Dispatchers.IO) {
            try {
                val info = launcherApps.getApplicationInfo(componentName.packageName, 0, user)
                val file = File(info.publicSourceDir)

                try {
                    file.copyTo(fileCopy, false)
                } catch (e: FileAlreadyExistsException) {
                    // Do nothing. If the file is already there we don't have to copy it again.
                }
            } catch (e: PackageManager.NameNotFoundException) {
            }
        }
        val shareIntent = Intent(Intent.ACTION_SEND)
        shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
        val uri = FileProvider.getUriForFile(
            context,
            context.applicationContext.packageName + ".fileprovider",
            fileCopy
        )
        shareIntent.putExtra(Intent.EXTRA_STREAM, uri)
        shareIntent.type = "application/vnd.android.package-archive"
        withContext(Dispatchers.Main) {
            context.startActivity(Intent.createChooser(shareIntent, null))
        }
    }

    override fun getActivityInfo(context: Context): ActivityInfo? {
        if (isAtLeastApiLevel(31)) {
            return launcherActivityInfo.activityInfo
        }
        return super.getActivityInfo(context)
    }

    companion object {
        private fun getStoreLinkForInstaller(
            installerPackage: String?,
            packageName: String?
        ): StoreLink? {
            if (packageName == null) return null
            return when (installerPackage) {
                "de.amazon.mShop.android", "com.amazon.venezia" -> {
                    StoreLink(
                        "Amazon App Shop",
                        "http://www.amazon.com/gp/mas/dl/android?p=${packageName}"
                    )
                }

                "com.android.vending" -> {
                    StoreLink(
                        "Google Play Store",
                        "https://play.google.com/store/apps/details?id=${packageName}"
                    )
                }

                "org.fdroid.fdroid", "com.aurora.adroid" -> {
                    StoreLink(
                        "F-Droid",
                        "https://f-droid.org/packages/${packageName}"
                    )
                }

                else -> null
            }
        }

        fun getPackageVersionName(context: Context, packageName: String): String? {
            return try {
                context.packageManager.getPackageInfo(packageName, 0).versionName
            } catch (e: PackageManager.NameNotFoundException) {
                null
            }
        }

        const val Domain = "app"
    }

    override fun getSerializer(): SearchableSerializer {
        return LauncherAppSerializer()
    }
}